﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Platform.ObjectModel.Design;
using System.Diagnostics;
using Platform.Storage.Impl;
using System.Reflection;
using Platform.Resources;
using Platform.Infrastructure;

namespace Platform.ObjectModel
{
	public static class LibraryUtility
	{
		/// <summary>
		/// Limites da biblioteca
		/// </summary>
		public static readonly LibraryLimits Limits = new LibraryLimits();

		/// <summary>
		/// Obtém o nome do campo	
		/// </summary>
		/// <param name="fullName"></param>
		/// <returns></returns>
		internal static string ExtractFieldName( string fullName )
		{
			if ( String.IsNullOrEmpty( fullName ) )
				return String.Empty;

			int start = fullName.IndexOf( '[' );

			if ( start > 0 )
			{
				int len = fullName.Length - start - 1;
				return fullName.Substring( start + 1, len );
			}

			start = fullName.LastIndexOf( '.' );
			if ( start > 0 )
				return fullName.Substring( start + 1 );
			else
				return fullName;
		}

		internal static Type GetSystemFieldType( FieldDescriptor field, DocumentFlake document )
		{
			Debug.Assert( field != null, "O argumento field não foi informado." );

			if ( document != null )
			{
				Type docType = document.GetType();
				PropertyInfo property = docType.GetProperty( document.SchemaName.Simplify( field.QualifiedName ) );

				if ( property != null )
					return property.PropertyType;
			}

			switch ( field.Type )
			{
				case FieldType.Enum:
				case FieldType.Text:
					return typeof( string );
				case FieldType.Number:
					return typeof( double );
				case FieldType.Boolean:
					return typeof( bool );
				case FieldType.DateTime:
					return typeof( DateTime );
				case FieldType.Collection:
					return typeof( ArrayList );
				case FieldType.Object:
					return typeof( DataObjectFlake );
			}

			throw Exceptions.InvalidFieldType( field, document.SchemaName );
		}

		

		/// <summary>
		/// Obtém o tipo do valor
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		public static FieldType GetFieldType( object value )
		{
			if ( value == null )
				return FieldType.Object;

			if ( value.GetType().IsEnum )
				return FieldType.Enum;

			switch ( Convert.GetTypeCode( value ) )
			{
				case TypeCode.Char:
				case TypeCode.String:
					return FieldType.Text;

				case TypeCode.Boolean:
					return FieldType.Boolean;

				case TypeCode.DateTime:
					return FieldType.DateTime;

				case TypeCode.Decimal:
				case TypeCode.Double:
				case TypeCode.Single:

				case TypeCode.Byte:
				case TypeCode.Int16:
				case TypeCode.Int32:
				case TypeCode.Int64:
				case TypeCode.SByte:
				case TypeCode.UInt16:
				case TypeCode.UInt32:
				case TypeCode.UInt64:
					return FieldType.Number;

				case TypeCode.DBNull:
				case TypeCode.Object:
				case TypeCode.Empty:
				default:
					if ( value is IEnumerable )
						return FieldType.Collection;
					else
						return FieldType.Object;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="objType"></param>
		/// <returns></returns>
		public static FieldType GetFieldType( Type objType )
		{
			if ( !objType.IsValueType )
			{
				if ( objType == typeof( string )
					|| objType == typeof( char )
					|| objType.Implements<IEnumerable<char>>() )
				{
					return FieldType.Text;
				}
				else if ( objType.Implements<IEnumerable>() )
				{
					return FieldType.Collection;
				}
			}
			else if ( objType == typeof( DateTime ) )
				return FieldType.DateTime;
			else if ( objType == typeof( bool ) )
				return FieldType.Boolean;
			else if ( objType.IsEnum )
				return FieldType.Enum;
			else
				return FieldType.Number;
			
			return FieldType.Object;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="parent"></param>
		/// <param name="relationName"></param>
		/// <returns></returns>
		internal static IEnumerable<DocumentFlake> GetChildren( DocumentFlake parent, DataObjectName relationName )
		{
			Debug.Assert( parent != null );
			Debug.Assert( relationName != null );

			object value = parent[ relationName ];

			if ( value == null )
				return Enumerable.Empty<DocumentFlake>();
			else if ( value is IEnumerable<DocumentFlake> )
				return ( IEnumerable<DocumentFlake> )value;
			else
				return new DocumentFlake[] { ( DocumentFlake )value };
		}

		internal static Schema GetSchema( this IQueryResolver resolver, Type entityType )
		{
			Schema s;
			if ( resolver.TryGetSchema( entityType, out s ) )
				return s;
			else
				throw new InvalidOperationException();
		}

		/// <summary>
		/// Define os limites da biblioteca
		/// </summary>
		public sealed class LibraryLimits
		{
			internal LibraryLimits()
			{ }

			/// <summary>
			/// Tamanho máximo de dados que um campo pode ter. Aproximadamente 4Mb
			/// </summary>
			/// <remarks>Tamanho máximo de um nvarchar</remarks>
			//public readonly int MaxFieldValueSize = 4 * 1024 * 1024; // ~4mb
			public readonly int MaxFieldValueSize = 4000;	

			/// <summary>
			/// Quantidade de relacionamentos que podem ser carregados em uma única chamada.
			/// </summary>
			/// <remarks>
			/// Se o documento possuir mais de "MaxEagerLoadRelations", somente os primeiros 
			/// registros serão carregados, até atingir "MaxEagerLoadRelations"
			/// </remarks>
			public readonly int MaxEagerLoadRelations = 512;

			/// <summary>
			/// Tamanho máximo do nome de uma entidade
			/// </summary>
			/// <remarks>Tamanho máximo para que não ultrapasse o tamanho máximo de um index no SQL Server (900bytes) quando combinado com outras colunas</remarks>
			public readonly int MaxQualifiedNameLen = 256;		

			/// <summary>
			/// Tamanho máximo de um token
			/// </summary>
			/// <remarks>Tamanho máximo para que não ultrapasse o tamanho máximo de um index no SQL Server (900bytes) quando combinado com outras colunas</remarks>
			public readonly int MaxTokenLength = 16;
		}
	}



}
